package com.tool.cn.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * zip压缩工具类
 * 
 * @author wangzh 2017年10月17日 下午3:54:19
 */
public class ZipAndGzUtil {
	private static final Log log = LogFactory.getLog(ZipAndGzUtil.class);
	
	/**
	 * 压缩文件为gz 
	 * @param sourceFileName
	 * @param gzipFileName
	 */
	public static void toGzipFile(String sourceFileName, String gzipFileName) {
		GZIPOutputStream out=null;
		BufferedInputStream input=null;
		try {
			// 创建zip输出流
			out = new GZIPOutputStream(new FileOutputStream(gzipFileName));
			// 建立gzip压缩文件输入流
			input = new BufferedInputStream(new FileInputStream(new File(sourceFileName)));

			int tag;
			// 将源文件写入到zip文件中
			while ((tag = input.read()) != -1) {
				out.write(tag);
			}
			
			out.flush();
		} catch (Exception e) {
			log.error("Gzip File :"+sourceFileName+" Error",e);
		}finally {
			try {
				if(input!=null) {
					input.close();
				}
				if(out!=null) {
					out.close();
				}
			} catch (IOException e) {
				log.error("Io Close Error:"+e.toString(),e);
			}
		}
	}
	
	/**
	 * 文件或文件夹tar打包
	 * @param sourceFile  源文件路径
	 * @param os  文件流
	 * @param basePath 归档包路径
	 */
	public static void tarFile(File sourceFile,TarArchiveOutputStream os,String basePath){
		try {
//			TarArchiveOutputStream os = new TarArchiveOutputStream(new FileOutputStream(tarFile));
			if(sourceFile.isDirectory()){
				File[] fileList = sourceFile.listFiles();
				if(fileList.length==0){
			        
					os.putArchiveEntry(new TarArchiveEntry(basePath+File.separator));
					os.closeArchiveEntry();
				}else{
					for (File file : fileList) {
						tarFile(file,os,basePath+File.separator+file.getName());
					}
				}
				
			}else{
				TarArchiveEntry entry = new TarArchiveEntry(basePath);  
			    entry.setSize(sourceFile.length());  
			    os.putArchiveEntry(entry);  
			    
			    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFile));  
			    int count;  
			    byte data[] = new byte[1024];  
			    while ((count = bis.read(data, 0, 1024)) != -1) {  
			        os.write(data, 0, count);  
			    }  
			  
			    bis.close();  
				os.closeArchiveEntry();
				
			}
			
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * gz文件解压
	 * @param sourcedir
	 */
	public static void unGzipFile(String sourceGzFile,String outputFile) {
		 GZIPInputStream input=null;
		 FileOutputStream output=null;
        try {
            //建立gzip解压工作流
           input = new GZIPInputStream(new FileInputStream(sourceGzFile));
            //建立解压文件输出流
            output= new FileOutputStream(outputFile);

           
            byte[] buf=new byte[1024];
            int len;
            
            while ((len = input.read(buf)) >0) {
                output.write(buf,0,len);
            }

           output.flush();
        } catch (Exception e){
            log.error(e.toString(),e);
        }finally{
        	try {
				if(input!=null) {
					input.close();
				}
				if(output!=null) {
					output.close();
				}
			} catch (IOException e) {
				log.error("Io Close Error:"+e.toString(),e);
			}
        }
    }

	/**
	 * tar.gz 文件解压
	 * @param gzfile tar.gz文件
	 * @param outputDir 输出目录
	 * @throws IOException
	 */
    public static void unTarGz(String gzfile,String outputDir) throws IOException{
    	
    	File sourceFile = new File(gzfile);
        TarArchiveInputStream fin = new TarArchiveInputStream(new GzipCompressorInputStream(new FileInputStream(sourceFile)));
        File extraceFolder = new File(outputDir);
        TarArchiveEntry entry;
        // 将 tar 文件解压到 extractPath 目录下
        while ((entry = fin.getNextTarEntry()) != null) {
            if (entry.isDirectory()) {
                continue;
            }
            File curfile = new File(extraceFolder, entry.getName());
            File parent = curfile.getParentFile();
            if (!parent.exists()) {
                parent.mkdirs();
            }
            // 将文件写出到解压的目录
            IOUtils.copy(fin, new FileOutputStream(curfile));
        }
    }
    
	/**
	 * 文件或文件夹打包为zip格式
	 * @param zipFileName
	 * @param sourceFileName
	 */
	public static void zip(String zipFileName, String sourceFileName) {
		try {
			// File zipFile = new File(zipFileName);
			log.info("压缩中...");

			// 创建zip输出流
			ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFileName));

			// 创建缓冲输出流
			BufferedOutputStream bos = new BufferedOutputStream(out);

			File sourceFile = new File(sourceFileName);

			// 调用函数
			compress(out, bos, sourceFile, sourceFile.getName());

			bos.close();
			out.close();
			log.info("压缩完成");
		} catch (FileNotFoundException e) {
			log.error(e, e);
		} catch (IOException e) {
			log.error(e, e);
		}

	}

	private static void compress(ZipOutputStream out, BufferedOutputStream bos, File sourceFile, String base) {
		try {
			// 如果路径为目录（文件夹）
			if (sourceFile.isDirectory()) {

				// 取出文件夹中的文件（或子文件夹）
				File[] flist = sourceFile.listFiles();

				if (flist.length == 0)// 如果文件夹为空，则只需在目的地zip文件中写入一个目录进入点
				{
					log.info(base + "/");
					out.putNextEntry(new ZipEntry(base +File.separator));
				} else// 如果文件夹不为空，则递归调用compress，文件夹中的每一个文件（或文件夹）进行压缩
				{
					for (int i = 0; i < flist.length; i++) {
						compress(out, bos, flist[i], base +File.separator + flist[i].getName());
					}
				}
			} else {
				out.putNextEntry(new ZipEntry(base));
				FileInputStream fos = new FileInputStream(sourceFile);
				BufferedInputStream bis = new BufferedInputStream(fos);

				int tag;
				log.info(base);
				// 将源文件写入到zip文件中
				while ((tag = bis.read()) != -1) {
					out.write(tag);
				}
				bis.close();
				fos.close();
			}
		} catch (FileNotFoundException e) {
			log.error(e, e);
		} catch (IOException e) {
			log.error(e, e);
		}
	}
	
	/**
	 * 
	 * zip解压
	 * @param srcFile     zip源文件
	 * @param destDirPath 解压后的目标文件夹
	 * @throws RuntimeException 解压失败会抛出运行时异常
	 */

	public static void unZip(File srcFile, String destDirPath) throws Exception {
		long start = System.currentTimeMillis();

		// 判断源文件是否存在
		if (!srcFile.exists()) {
			throw new RuntimeException(srcFile.getPath() + "所指文件不存在");

		}

		// 开始解压
		ZipFile zipFile = null;

		try {

			zipFile = new ZipFile(srcFile);
			Enumeration<?> entries = zipFile.entries();
			while (entries.hasMoreElements()) {
				ZipEntry entry = (ZipEntry) entries.nextElement();
				System.out.println("解压" + entry.getName());
				// 如果是文件夹，就创建个文件夹
				if (entry.isDirectory()) {
					String dirPath = destDirPath + "/" + entry.getName();
					File dir = new File(dirPath);
					dir.mkdirs();
				} else {

					// 如果是文件，就先创建一个文件，然后用io流把内容copy过去
					File targetFile = new File(destDirPath + "/" + entry.getName());
					// 保证这个文件的父文件夹必须要存在
					if (!targetFile.getParentFile().exists()) {
						targetFile.getParentFile().mkdirs();
					}
					targetFile.createNewFile();
					// 将压缩文件内容写入到这个文件中
					InputStream is = zipFile.getInputStream(entry);
					FileOutputStream fos = new FileOutputStream(targetFile);

					int len;
					byte[] buf = new byte[1024];
					while ((len = is.read(buf)) != -1) {
						fos.write(buf, 0, len);
					}

					// 关流顺序，先打开的后关闭
					fos.close();
					is.close();
				}

			}

			long end = System.currentTimeMillis();
			System.out.println("解压完成，耗时：" + (end - start) + " ms");

		} catch (Exception e) {

			throw new RuntimeException("unzip error from ZipUtils", e);

		} finally {

			if (zipFile != null) {
				try {
					zipFile.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

}
